home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue55 / Contain / FlexiSort.pas next >
Encoding:
Pascal/Delphi Source File  |  2000-02-02  |  6.4 KB  |  273 lines

  1. unit FlexiSort;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, TypInfo, Classes, Dialogs;
  7.  
  8. type
  9.  
  10. {$TYPEINFO ON}
  11.   TRTTIObject = class (TObject)
  12.   end;
  13. {$TYPEINFO OFF}
  14.  
  15.   EFlexiSortListError = class(Exception);
  16.  
  17.   TSortItem = record
  18.     PPI: PPropInfo;
  19.     Descending: Boolean;
  20.     Kind: TTypeKind;
  21.   end;
  22.  
  23.   TSortItems = array of TSortItem;
  24.  
  25.   TFlexiSortList = class(TObject)
  26.   private
  27.     ContainedClassType: TClass;
  28.     List: TList;
  29.     SortItems: TSortItems;
  30.   protected
  31.     function CompareItems(Item1, Item2: Pointer): Integer;
  32.     function Get(Index: Integer): TObject;
  33.     function GetCapacity: Integer;
  34.     function GetCount: Integer;
  35.     procedure InitializeSortItems(const SortFields: array of String);
  36.     procedure Put(Index: Integer; Item: TObject);
  37.     procedure QuickFlexiSort(SortList: PPointerList; L, R: Integer);
  38.     procedure SetCapacity(NewCapacity: Integer);
  39.     procedure SetCount(NewCount: Integer);
  40.   public
  41.     constructor Create(ClassType: TClass);
  42.     destructor Destroy; override;
  43.     function Add(Item: TObject): Integer;
  44.     procedure Clear;
  45.     procedure Delete(Index: Integer);
  46.     procedure Pack;
  47.     function Remove(Item: TObject): Integer;
  48.     procedure Sort(const SortFields: array of String);
  49.     property Capacity: Integer read GetCapacity write SetCapacity;
  50.     property Count: Integer read GetCount write SetCount;
  51.     property Items[Index: Integer]: TObject read Get write Put; default;
  52.   end;
  53.  
  54. implementation
  55.  
  56. { TFlexiSortList }
  57.  
  58. function TFlexiSortList.Add(Item: TObject): Integer;
  59. begin
  60.   if Item.ClassType <> ContainedClassType then
  61.     raise EFlexiSortListError.Create('Add attempted for object not ' +
  62.         'of class type ' + ContainedClassType.ClassName);
  63.   Result := List.Add(Item);
  64. end;
  65.  
  66. procedure TFlexiSortList.Clear;
  67. begin
  68.   List.Clear;
  69. end;
  70.  
  71. function TFlexiSortList.CompareItems(Item1, Item2: Pointer): Integer;
  72. var
  73.   I: Integer;
  74.   PPI: PPropInfo;
  75.  
  76.   function CompareOrd(I1, I2: LongInt): Integer;
  77.   begin
  78.     if I1 > I2 then
  79.       Result := 1
  80.     else if I1 = I2 then
  81.       Result := 0
  82.     else
  83.       Result := -1;
  84.     end;
  85.  
  86.   function CompareFloat(I1, I2: Extended): Integer;
  87.   begin
  88.     if I1 > I2 then
  89.       Result := 1
  90.     else if I1 = I2 then
  91.       Result := 0
  92.     else
  93.       Result := -1;
  94.     end;
  95.  
  96.   function CompareInt64(I1, I2: Int64): Integer;
  97.   begin
  98.     if I1 > I2 then
  99.       Result := 1
  100.     else if I1 = I2 then
  101.       Result := 0
  102.     else
  103.       Result := -1;
  104.     end;
  105.  
  106. begin
  107.   Result := 0;
  108.   I := 0;
  109.   while ((Result = 0) and (I < Length(SortItems))) do
  110.   begin
  111.     PPI := SortItems[I].PPI;
  112.     if PPI <> nil then
  113.     begin
  114.       case SortItems[I].Kind of
  115.         tkInteger,
  116.         tkChar,
  117.         tkEnumeration:
  118.           Result := CompareInt64(GetOrdProp(Item1, PPI),
  119.               GetOrdProp(Item2, PPI));
  120.         tkFloat:
  121.           Result := CompareFloat(GetFloatProp(Item1, PPI),
  122.               GetFloatProp(Item2, PPI));
  123.         tkString,
  124.         tkLString,
  125.         tkWString:
  126.           Result := AnsiCompareStr(GetStrProp(Item1, PPI),
  127.               GetStrProp(Item2, PPI));
  128.         tkInt64:
  129.           Result := CompareInt64(GetInt64Prop(Item1, PPI),
  130.               GetInt64Prop(Item2, PPI));
  131.       end;
  132.     end;
  133.     if Result = 0 then
  134.       Inc(I)
  135.     else if SortItems[I].Descending then
  136.       Result := -Result;
  137.   end;
  138. end;
  139.  
  140. constructor TFlexiSortList.Create(ClassType: TClass);
  141. begin
  142.   inherited Create;
  143.   List := TList.Create;
  144.   ContainedClassType := ClassType;
  145. end;
  146.  
  147. procedure TFlexiSortList.Delete(Index: Integer);
  148. begin
  149.   List.Delete(Index);
  150. end;
  151.  
  152. destructor TFlexiSortList.Destroy;
  153. begin
  154.   List.Free;
  155.   inherited Destroy;
  156. end;
  157.  
  158. function TFlexiSortList.Get(Index: Integer): TObject;
  159. begin
  160.   Result := TObject(List[Index]);
  161. end;
  162.  
  163. function TFlexiSortList.GetCapacity: Integer;
  164. begin
  165.   Result := List.Capacity;
  166. end;
  167.  
  168. function TFlexiSortList.GetCount: Integer;
  169. begin
  170.   Result := List.Count;
  171. end;
  172.  
  173. procedure TFlexiSortList.InitializeSortItems(
  174.     const SortFields: array of String);
  175. var
  176.   I: Integer;
  177.   S: String;
  178.   PPI: PPropInfo;
  179.   PTI: PTypeInfo;
  180.   TK: TTypeKind;
  181. begin
  182.   SetLength(SortItems, High(SortFields) + 1);
  183.   for I := 0 to High(SortFields) do
  184.   begin
  185.     SortItems[I].PPI := nil;
  186.     SortItems[I].Descending := False;
  187.     if Copy(SortFields[I], 1, 2) = 'D:' then
  188.     begin
  189.       SortItems[I].Descending := True;
  190.       S := Copy(SortFields[I], 3, $7FFF);
  191.     end
  192.     else if Copy(SortFields[I], 1, 2) = 'A:' then
  193.       S := Copy(SortFields[I], 3, $7FFF)
  194.     else
  195.       S := SortFields[I];
  196.     PPI := GetPropInfo(ContainedClassType.ClassInfo, S);
  197.     if PPI = nil then
  198.       raise EFlexiSortListError.Create('Sort item ' + S +
  199.           ' is not a published property for ' +
  200.           ContainedClassType.ClassName);
  201.     SortItems[I].PPI := PPI;
  202.     PTI := PPI.PropType^;
  203.     TK := PTI.Kind;
  204.     if not (TK in [tkInteger, tkChar, tkEnumeration, tkFloat,
  205.         tkString, tkLString, tkWString, tkInt64]) then
  206.       raise EFlexiSortListError.Create('Sort item ' + S +
  207.           ' is not a valid type for sorting in class ' +
  208.           ContainedClassType.ClassName);
  209.     SortItems[I].Kind := TK;
  210.   end;
  211. end;
  212.  
  213. procedure TFlexiSortList.Pack;
  214. begin
  215.   List.Pack;
  216. end;
  217.  
  218. procedure TFlexiSortList.Put(Index: Integer; Item: TObject);
  219. begin
  220.   List[Index] := Item;
  221. end;
  222.  
  223. procedure TFlexiSortList.QuickFlexiSort(SortList: PPointerList;
  224.     L, R: Integer);
  225. var
  226.   I, J: Integer;
  227.   P, T: Pointer;
  228. begin
  229.   repeat
  230.     I := L;
  231.     J := R;
  232.     P := SortList^[(L + R) shr 1];
  233.     repeat
  234.       while CompareItems(SortList^[I], P) < 0 do Inc(I);
  235.       while CompareItems(SortList^[J], P) > 0 do Dec(J);
  236.       if I <= J then
  237.       begin
  238.         T := SortList^[I];
  239.         SortList^[I] := SortList^[J];
  240.         SortList^[J] := T;
  241.         Inc(I);
  242.         Dec(J);
  243.       end;
  244.     until I > J;
  245.     if L < J then QuickFlexiSort(SortList, L, J);
  246.     L := I;
  247.   until I >= R;
  248. end;
  249.  
  250. function TFlexiSortList.Remove(Item: TObject): Integer;
  251. begin
  252.   Result := List.Remove(Item);
  253. end;
  254.  
  255. procedure TFlexiSortList.SetCapacity(NewCapacity: Integer);
  256. begin
  257.   List.Capacity := NewCapacity;
  258. end;
  259.  
  260. procedure TFlexiSortList.SetCount(NewCount: Integer);
  261. begin
  262.   List.Count := NewCount;
  263. end;
  264.  
  265. procedure TFlexiSortList.Sort(const SortFields: array of String);
  266. begin
  267.   InitializeSortItems(SortFields);
  268.   if List.Count > 0 then
  269.     QuickFlexiSort(List.List, 0, (List.Count - 1));
  270. end;
  271.  
  272. end.
  273.